/**********************************************************************
*  Copyright (c) 2007 - Victor Jacobs - victor.jacobs@gmail.com
*
*  Permission is hereby granted, free of charge, to any person
*  obtaining a copy of this software and associated documentation
*  files (the "Software"), to deal in the Software without
*  restriction, including without limitation the rights to use,
*  copy, modify, merge, publish, distribute, sublicense, and/or sell
*  copies of the Software, and to permit persons to whom the
*  Software is furnished to do so, subject to the following
*  conditions:
*
*  The above copyright notice and this permission notice shall be
*  included in all copies or substantial portions of the Software.
*
*  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
*  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
*  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
*  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
*  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
*  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
*  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
*  OTHER DEALINGS IN THE SOFTWARE.
**********************************************************************/

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace Vj.Ann
{
    public partial class GraphControl : UserControl
    {
        #region Data Fields

        private Graphics myGraphic;

        #endregion

        #region Properties

        private BaseNetwork network;

        public BaseNetwork Network
        {
            get { return network; }
            set
            {
                network = value;
                this.Refresh();
            }
        }

        private float scale = 1.0f;

        public float ScaleValue
        {
            get { return scale; }
            set
            {
                scale = value;
                this.Refresh();
            }
        }

        private bool drawNetwork = false;

        public bool DrawNetwork
        {
            get { return drawNetwork; }
            set
            {
                drawNetwork = value;
                this.Refresh();
            }
        }

        private bool drawOnlyOutputValues = false;

        public bool DrawOnlyOutputValues
        {
            get { return drawOnlyOutputValues; }
            set
            {
                drawOnlyOutputValues = value;

                if (value == true)
                    drawAllValues = false;

                this.Refresh();
            }
        }

        private bool drawAllValues = false;

        public bool DrawAllValues
        {
            get { return drawAllValues; }
            set
            {
                drawAllValues = value;

                if (value == true)
                    drawOnlyOutputValues = false;

                this.Refresh();
            }
        }

        #endregion

        #region Constructor

        public GraphControl()
        {
            InitializeComponent();
        }

        #endregion

        #region Draw Network Method (break this up asap)

        public void DrawNet(Graphics g)
        {
            this.Controls.Clear();

            #region Determine layer with most nodes

            int[] numOfNodes = new int[network.Layers.Count];

            int largestLayer = 0;

            numOfNodes[0] = network.Layers[0].Neurons.Count;

            for (int i = 1; i < network.Layers.Count; i++)
            {
                numOfNodes[i] = network.Layers[i].Neurons.Count;

                if (numOfNodes[i] > numOfNodes[i - 1])
                    largestLayer = i;
            }

            #endregion

            #region Draw the nodes

            float sizeOfNode = 20.0f;

            //draw nodes
            for (int i = 0; i < network.Layers.Count; i++)
            {
                int leftPadding = 0;

                if (network.Layers[i].Neurons.Count < network.Layers[largestLayer].Neurons.Count)
                {
                    int diff = network.Layers[largestLayer].Neurons.Count - network.Layers[i].Neurons.Count;
                    leftPadding = diff * 20;
                }

                for (int j = 0; j < network.Layers[i].Neurons.Count; j++)
                {
                    Point currCenter = new Point(25 + leftPadding + 50 * j, 50 + 75 * i);
                    network.Layers[i].Neurons[j].Center = currCenter;

                    if (drawNetwork)
                    {
                        if (network.Layers[i].Neurons[j].Type == NeuronType.Bias)
                            g.DrawEllipse(Pens.Red, (float)currCenter.X - sizeOfNode / 2.0f, (float)currCenter.Y - sizeOfNode / 2.0f, sizeOfNode, sizeOfNode);
                        else
                            g.DrawEllipse(Pens.Black, (float)currCenter.X - sizeOfNode / 2.0f, (float)currCenter.Y - sizeOfNode / 2.0f, sizeOfNode, sizeOfNode);
                    }
                }
            }

            #endregion

            #region Draw connections

            if (drawNetwork)
            {

                // draw connections
                for (int i = 0; i < network.Layers.Count - 1; i++)
                {
                    for (int j = 0; j < network.Layers[i].Neurons.Count; j++)
                    {
                        for (int z = 0; z < network.Layers[i].Neurons[j].Connections.Count; z++)
                        {
                            Point pt1 = network.Layers[i].Neurons[j].Connections[z].StartNode.Center;
                            Point pt2 = network.Layers[i].Neurons[j].Connections[z].EndNode.Center;

                            if (network.Layers[i].Neurons[j].Connections[z].StartNode.Type == NeuronType.Bias)
                                g.DrawLine(Pens.LightBlue, pt1, pt2);
                            else
                                g.DrawLine(Pens.Blue, pt1, pt2);
                        }
                    }
                }
            }
            #endregion
        }

        private void DrawValues()
        {
            //draw nodes
            for (int i = 0; i < network.Layers.Count; i++)
            {
                for (int j = 0; j < network.Layers[i].Neurons.Count; j++)
                {
                    if (drawOnlyOutputValues)
                    {
                        if (i == network.Layers.Count - 1) // output layer
                        {
                            Label tmpLabel = new Label();
                            Point center = network.Layers[i].Neurons[j].Center;

                            center.X -= 10;
                            center.Y += 12;

                            tmpLabel.Location = center;
                            tmpLabel.Size = new Size(30, 13);
                            tmpLabel.Text = network.Layers[i].Neurons[j].Output.ToString("N3");
                            tmpLabel.SendToBack();
                            this.Controls.Add(tmpLabel);
                        }
                    }
                    else if (drawAllValues) // draw all values
                    {
                        Label tmpLabel = new Label();
                        Point center = network.Layers[i].Neurons[j].Center;

                        center.X -= 10;
                        center.Y += 12;

                        tmpLabel.Location = center;
                        tmpLabel.Size = new Size(30, 13);
                        tmpLabel.Text = network.Layers[i].Neurons[j].Output.ToString("N3");
                        tmpLabel.SendToBack();
                        this.Controls.Add(tmpLabel);
                    }
                    else { /* draw nothing */ }
                }
            }

            ScaleLabels();
        }

        #endregion

        #region OnPaint

        private void GraphPictureBox_Paint(object sender, PaintEventArgs e)
        {
            Bitmap drawing = null;
            drawing = new Bitmap(this.Width, this.Height, e.Graphics);
            myGraphic = Graphics.FromImage(drawing);

            myGraphic.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;

            myGraphic.ScaleTransform(scale, scale);

            if (network != null)
            {
                DrawNet(myGraphic);
                DrawValues();
            }

            e.Graphics.DrawImageUnscaled(drawing, 0, 0);
            myGraphic.Dispose();
        }

        #endregion

        #region Utility Methods

        private void ScaleLabels()
        {
            ControlCollection cc = this.Controls;

            foreach (Control c in cc)
            {
                Point loc = c.Location;
                loc.X = (int)((float)loc.X * scale);
                loc.Y = (int)((float)loc.Y * scale);
                c.Location = loc;
            }

        }

        #endregion

    }
}
